#pragma once
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
#include<cassert>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define DEV  "/dev/null"

void daemonSelf(const char* CurrPath = nullptr)
{
    //1.让调用该函数的进程，屏蔽掉异常的信号！
    //什么叫做异常的信号呢？——比如说客户端给服务端发送一个消息！
    //服务端请求完这个消息正准备响应会去，这时候客户端崩溃了！那么这时候相当于给一个已经关闭的文件描述符进行写入！
    //那么就像是管道读写一样！如果读端关闭！写端存在！那么操作系统就会发送SIGPIPE信号杀死写端！这里也是同样如此！
    //所以我们要忽略掉一些系统信号！
    signal(SIGPIPE,SIG_IGN);//忽略SIGPIPE,防止错误写入！


    //2.我们要调用setsid！但是setsid是不能直接调用的！操作系统不允许！直接调用会报错！
    //为什么呢？——为的就是防止组长调用setsid！
    //所以我们要如何让自己不是组长！
    //一般在一个进程里面谁早谁就是组长！
    if(fork()>0) exit(0);//父进程直接退出！子进程变成孤儿进程

    //走到这里的肯定是子进程！——子进程就一定不是组组长！
    //守护进程，又叫做精灵进程——是孤儿进程的一种！
    pid_t n = setsid();
    assert(n != -1);

    //3.守护进程是脱离终端的！所以我们要关闭或重定向以前默认打开的文件！
    //因为是脱离终端的是不需要关心键盘或者显示器上的任何时事件所以012号的描述符应该关闭或者重定向！
    //此时只有用网络端口才能访问到这个进程！
    //我们最好对012的文件描述符进行重定向！而不是关闭！
    //万一我们有些日志没有处理干净是往显示器上打印的怎么办？对一个已经关闭的文件描述符写入，会立马报错导致进程挂掉了！
    //在linux下我们可以向/dev/null这个文件进行重定向！
    //这个文件像是一个黑洞，默认处理数据的方式就是凡是写入的都抛弃
    //我们读取的时候，即不阻塞，但是也什么都没有！
    int fd = open(DEV,O_RDWR);
    if(fd >= 0)
    {
        dup2(fd,0);
        dup2(fd,1);
        dup2(fd,2);

        close(fd);//以后就不需要用了
    }
    else 
    {

        close(0);
        close(1);
        close(2);
    }


    //4.可选，进程的执行路径是否更改！——即修改当前进程的当前路径
    if(CurrPath) chdir(CurrPath);

}
